package agents.resource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import agents.Ant;
import domain.TrafficInfrastructure;


public class FeasibilityAnt implements Ant {
	
	private static final int MAX_TABLE_LENGTH = 10;
	
	//constructor voor totaal nieuwe f-ant
	public FeasibilityAnt(ResourceAgent origin) {
		distanceTable = new ArrayList<ResourceDistance>();
		currentResource = origin; 
	}
	
	//constructor voor gekloonde ant
	private FeasibilityAnt(FeasibilityAnt oldAnt, ResourceAgent newResource) {
		distanceTable = new ArrayList<ResourceDistance>(oldAnt.distanceTable);
		currentResource = newResource;
	}
	
	
	/* *** stap uitvoeren *** */
	
	// passeert traffic infrastructure, update met lengtes van resources en verwittigt deze
	// die update dan zijn routeringstabel, vervolgens verwijdert ant oudste uit tabel als deze vol zit
	public void doStep() {
		TrafficInfrastructure infr = currentResource.getTrafficInfrastructure();
		
		//als ant loop gemaakt heeft, verwijder dubbele info
		if(hasInDistanceTable(infr)) {
			removeEntriesBeforeAnd(infr);
		}
		
		boolean shouldStop = false;
		
		Collection<TrafficInfrastructure> outs = new ArrayList<TrafficInfrastructure>();
		for(ResourceAgent ra : currentResource.getOutgoingResources()) {
			outs.add(ra.getTrafficInfrastructure());
		}
		
		distanceTable.add(new ResourceDistance(infr,infr.getLength(),outs));
		if(distanceTable.size() != 1) { //niks bij beginresource van de ant doen
			currentResource.notifyFeasibilityAntArrival(this);
			shouldStop = currentResource.hasNoUsefulInformation(this);
			if(distanceTable.size() > MAX_TABLE_LENGTH) {
				distanceTable.remove(0); //oudste element verwijderen als tabel vol is
			}
		}
		
		if(!shouldStop) {			//ga naar volgende resources
			createChildAnts();
		}
		
		terminate();
	}

	private void removeEntriesBeforeAnd(TrafficInfrastructure infr) {
		List<ResourceDistance> resDistances = new ArrayList<ResourceDistance>(distanceTable);
		for(ResourceDistance resDis : resDistances) {
			if(resDis.getResource() == infr) {
				distanceTable.remove(resDis);
				return;
			}
			distanceTable.remove(resDis);
		}
	}

	private void createChildAnts() {
		Collection<ResourceAgent> ins = currentResource.getIngoingResources();
		for(ResourceAgent in : ins) { //tegen de stroom in zwemmen
			childAnts.add(new FeasibilityAnt(this,in));
		}
	}
	
	
	/* *** communicatie geïnitieerd door ResourceAgents *** */
	
	//lijst van gepasseerde resources, laatste (dichtsbijzijnde) eerst
	//op moment van notificatie van een resource (notifyFeasibilityAntArrival) staat deze nog niet in de lijst
	public List<TrafficInfrastructure> getPassedTrafficInfrastructure() {
		List<TrafficInfrastructure> passedInfrastructure = new ArrayList<TrafficInfrastructure>();
		for(ResourceDistance crd : distanceTable) {
			passedInfrastructure.add(0,crd.getResource()); //keer volgorde crossroads om
		}
		return passedInfrastructure;
	}

	//geeft afstand van resource waarop feasibility ant zich bevindt tot een opgegeven resource waar de ant gepasseerd is
	//lengte van huidige resource en doelinfrastructure zijn niet in teruggegeven 
	public float getDistanceTo(TrafficInfrastructure infr) {
		float totalDistance = 0;

		int indexInfr = getIndexInDistanceTableOf(infr);

		if(indexInfr == -1) throw new IllegalArgumentException("feasibility-ant heeft opgegeven resource niet gepasseerd");
		if(infr == currentResource.getTrafficInfrastructure()) return 0; //als ant op opgegeven resource zit
		
		for(int i = indexInfr; i<distanceTable.size()-1; i++) {
			TrafficInfrastructure passedInfr = distanceTable.get(i).getResource();
			totalDistance += passedInfr.getLength();
		}
				
		return totalDistance;
	}
	
	private boolean hasInDistanceTable(TrafficInfrastructure infr) {
		return getIndexInDistanceTableOf(infr) != -1;
	}
	
	private int getIndexInDistanceTableOf(TrafficInfrastructure infr) {
		return distanceTable.indexOf(getResourceDistanceOf(infr)) ;
	}
	
	private ResourceDistance getResourceDistanceOf(TrafficInfrastructure infr) {
		for(ResourceDistance resDis : distanceTable) {
			if(resDis.getResource() == infr) return resDis;
		}
		return null;
	}
	
	/* **** terminatie **** */
	
	//stopt ant
	public void terminate() {
		isTerminated = true;
	}
	
	public boolean isTerminated() {
		return isTerminated;
	}
	
	private boolean isTerminated = false;
	
	private class ResourceDistance {
		public ResourceDistance(TrafficInfrastructure c, float i, Collection<TrafficInfrastructure> outs) {
			this.resource = c;
			this.distance = i;
			this.outgoingResources = new ArrayList<TrafficInfrastructure>(outs);
		}
		
		public TrafficInfrastructure getResource() {
			return resource;
		}
		
		public float getLength() {
			return distance;
		}
		
		public Collection<TrafficInfrastructure> getOutgoingNeighbours() {
			return new ArrayList<TrafficInfrastructure>(outgoingResources);
		}
		
		private final TrafficInfrastructure resource;
		private final float distance;
		private final Collection<TrafficInfrastructure> outgoingResources;
	}
	
	/* *** child ants *** */

	public List<Ant> getChildAnts() {
		return childAnts;
	}
	
	private List<Ant> childAnts = new ArrayList<Ant>();
	
	
	/* *** huidige resource agent *** */
	
	private ResourceAgent currentResource;
	
	/* *** Tabel met afstanden *** */
	
	private List<ResourceDistance> distanceTable;
	
	public List<ResourceDistance> getDistanceTable() {
		return distanceTable;
	}

	public Collection<TrafficInfrastructure> getOutgoingResourcesOf(TrafficInfrastructure infr) {
		ResourceDistance resDis = getResourceDistanceOf(infr);
		return resDis.getOutgoingNeighbours();
	}

}
